
import os
import json

import subprocess
import xml.etree.ElementTree as ET

exe_path = 'E:\\Work\\UnitTest\\Examples\\CodeAnalysis\\'
mingw_path = 'E:\\Work\\UnitTest\\Examples\\Host\\MinGW\\'
temp_path = 'E:\\Work\\UnitTest\\temp\\'
gcc_path = 'E:\\Work\\UnitTest\\Examples\\Host\\MinGW\\bin\\'

# 生成测试代码
#  对项目所有源码进行插桩，对指定函数进行覆盖率插桩
def gen_testcode(project, testcase, procedure, target_path):
    cur_path = os.path.dirname(__file__)

    # 创建临时输出目录
    if not os.path.exists(temp_path+'ii_out'):
        os.makedirs(temp_path+'ii_out')

    procedure_usr = procedure.get_prop('usr')

    files = project.get_allchildren_by_kind('File')
    for f in files:
        # 对每一个源码文件进行插桩
        file_path = f.get_prop('path')
        file_name = os.path.basename(file_path)

        args = []
        args.append(exe_path + "RTInsightII\\Cti_RTII.exe")
        args.append("-Source")
        args.append(file_path)
        args.append("-PreprocessFile")
        args.append(file_path)
        args.append("-Compiler")
        args.append("gcc")
        args.append("-TargetObj")
        args.append(temp_path+"analysis_out\\"+file_name.split('.')[0])
        args.append("-OutputSource")
        args.append(temp_path+'ii_out\\'+file_name)
        args.append("-IncludeFuns")
        args.append(procedure_usr)

        rc = subprocess.Popen(args)
        rc.wait()


    
# 生成驱动
def gen_drivercode(project, testcase, procedure, target_path):
    '''
    #define XX_Fun  // 激活函数使用
    #define _VISIONMC_UT_GLOBAL_

    #include "./loop.c"

    void main() {
        // 输入参数构造
        int t1 = 20;
        int t2 = 30;

        // 调用原始函数
        funx(t1, t2);
    }
    '''

    re = ""

    # 激活函数宏
    re += "#define _VISIONMC_UT_GLOBAL_\n\n"

    # 插桩输出
    re += '''
#include <stdio.h>
char g_buf[100] = {0};
int OutputXX(int id) {
    g_buf[id>>3] |= (0x01 << (id&0x7));
    return 0;
}
void OutputSave(char* s_file) {
    FILE* fp = fopen(s_file, "wb+");
    if (fp) {
        fwrite(g_buf, 1, 100, fp);
        fclose(fp);
    }
}
    '''

    # 插桩函数
    calls = procedure.get_prop('calls')
    calls = json.loads(calls)
    calls = [call['usr'] for call in calls]

    procedures = project.get_allchildren_by_kind('Procedure')
    usr2procedure = {}
    for pro in procedures:
        usr2procedure[pro.get_prop('usr')] = pro

    for call in calls:
        if testcase.get_prop(call+"[call]") == "True" or testcase.get_prop(call+"[call]") == "False":
            continue

        fun_name = ""
        if call in usr2procedure:
            fun_name = usr2procedure[call].get_name()
        re += "\n#define _FUN_VISIONMC_"+fun_name+"\n"
        re += '\n'+testcase.get_prop(call+"[call]")+'\n'


    # 包含文件
    file_path = procedure.parent().get_prop('path')
    files = [temp_path+'ii_out\\'+os.path.basename(file_path)]
    
    rsplit = '\\'
    slit = '/'
    inc_files = [f'''#include "{f.replace(rsplit,slit)}"''' for f in files]
    re += '\n'.join(inc_files)


    # 主函数
    re += "\nvoid main() {\n"

    #   输入
    in_vars = procedure.get_prop('inputs')
    #out_vars = procedure.get_prop('outputs')

    in_vars = json.loads(in_vars)
    #out_vars = json.loads(out_vars)

    #for v in in_vars:
    #    node.set_prop(v['usr']+"[in]", "")
    #for v in out_vars:
    #    node.set_prop(v['usr']+"[out]", "")
    #break

    index = 0
    var_usr_2_local = {}
    for var in in_vars:
        vname = "t"+str(index)
        vtype = var['type_str']
        vval  = testcase.get_prop(var['usr']+'[in]')
        var_usr_2_local[var['usr']] = vname
        index += 1

        re += '\t' + f'''{vtype} {vname} = {vval};\n'''
    
    # 被测函数调用
    params = procedure.get_prop('params')
    params = json.loads(params)

    vparams = [f"{var_usr_2_local[para['usr']]}" for para in params]
    vparams = ','.join(vparams)

    re += f'''\t{procedure.get_prop('name')}({vparams});\n'''

    # 保存插桩结果
    bitmap_bin = (temp_path+'ii_out\\bitmap.bin').replace('\\', '/')
    re += f'''\tOutputSave("{bitmap_bin}");\n'''

    re += "}\n"

    with open(temp_path+'ii_out\\driver_main.c', 'w+') as fp:
        fp.write(re)
    #print(re)

   

# 编译
def build(project, testcase, procedure, target_path):
    if 'vmc_ut' not in os.environ:
        os.environ['vmc_ut'] = '1'
        os.environ['PATH'] = gcc_path+";"+os.environ['PATH']
    
    args = []
    args.append('gcc')
    args.append(temp_path+'ii_out\\driver_main.c')
    args.append('-o')
    args.append(temp_path+'ii_out\\test.exe')

    rc = subprocess.Popen(args)
    rc.wait()

# 监控、执行
def run(project, testcase, procedure, target_path):
    args = []
    args.append(temp_path+'ii_out\\test.exe')

    rc = subprocess.Popen(args)
    rc.wait()

def ToBinStr(v):
    s = bin(v)[2:]
    if len(s) < 8:
        for i in range(0, 8-len(s)):
            s = '0'+s
    return s

# 覆盖率读取
def get_coverage(project, testcase, procedure, target_path):
    # 解析bitmap文件
    ibrs = []
    with open(temp_path+'ii_out\\bitmap.bin', 'rb') as fp:
        buf = fp.read()
    
        s_buf = ""
        for v in buf:
            s_buf += "".join(ToBinStr(v)[::-1])
    
        index = 0
        for v in s_buf:
            if v == '1':
                ibrs.append(index)
            index += 1

    # 解析MIF文件
    base_file = os.path.basename(procedure.parent().get_prop('path')).split('.')[0]
    with open(temp_path+'analysis_out\\'+base_file+'.mif', 'r', encoding='utf-8') as fp:
        buf = fp.read()
        buf.replace('encoding="GB2312"', 'encoding="UTF-8"')
        
        root = ET.fromstring(buf)
        #root = tree.getroot()

    infos = {}
    infos['Statements'] = {}

    
    statements = root.findall('.//Statement')
    for statement in statements:
        execute = statement.attrib['ExcuteStatement']
        if execute != 'true':
            continue

        ref = statement.attrib['REFLine']
        infos['Statements'][int(ref)] = False
    
    ibr2stmt = {}
    blocks = root.findall('.//Block')
    for block in blocks:
        afrom = block.attrib['From']
        ato = block.attrib['To']
        aibr = block.attrib['IBR']
        for l in range(int(afrom), int(ato)+1):
            if l in infos['Statements']:
                if int(aibr) in ibr2stmt:
                    ibr2stmt[int(aibr)].append(l)
                else:
                    ibr2stmt[int(aibr)] = []
                    ibr2stmt[int(aibr)].append(l)
    
    # 根据ibr进行行标记
    for ibr in ibrs:
        if ibr not in ibr2stmt:
            continue

        for line in ibr2stmt[ibr]:
            infos['Statements'][line] = True


    # 按函数进行覆盖率结果统计
    procedures = root.findall('.//Procedure')
    for proc in procedures:
        start = int(proc.attrib['Start'])
        end = int(proc.attrib['End'])

        count = 0
        execute = 0
        for l in range(start, end+1):
            if l not in infos['Statements']:
                continue

            count += 1
            if infos['Statements'][l] == True:
                execute += 1
        
        # 目前只更新当前测试函数覆盖率
        if proc.attrib['ProcedureID'] == procedure.get_prop('usr'):
            if count==0:
                count = 1
            procedure.set_prop('cc_cov', str(execute/count))


# 用例执行状态读取
def get_status(project, testcase, procedure, target_path):
    pass